/**
 * @file pcm_to_wav.c
 * @author haier Max (shangbaorun@haier.com)
 * @brief 
 * @version 0.1
 * @date 2022-02-21
 * 
 * @copyright Copyright (c) 2022
 * 
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "pcm_to_wav.h"

/**
 * @brief wav head info
 * 
 */
struct wav_header {
    uint32_t riff_id;
    uint32_t riff_sz;
    uint32_t riff_fmt;
    uint32_t fmt_id;
    uint32_t fmt_sz;
    uint16_t audio_format;
    uint16_t num_channels;
    uint32_t sample_rate;
    uint32_t byte_rate;
    uint16_t block_align;
    uint16_t bits_per_sample;
    uint32_t data_id;
    uint32_t data_sz;
};

/** Determines the number of bits occupied by a @ref pcm_format.
 * @param format A PCM format.
 * @return The number of bits associated with @p format
 * @ingroup libtinyalsa-pcm
 */
unsigned int haier_pcm_format_to_bits(enum pcm_format_haier format)
{
    switch (format) {
    case HAIER_PCM_FORMAT_S32_LE:
    case HAIER_PCM_FORMAT_S32_BE:
    case HAIER_PCM_FORMAT_S24_LE:
    case HAIER_PCM_FORMAT_S24_BE:
    case HAIER_PCM_FORMAT_FLOAT_LE:
    case HAIER_PCM_FORMAT_FLOAT_BE:
        return 32;
    case HAIER_PCM_FORMAT_S24_3LE:
    case HAIER_PCM_FORMAT_S24_3BE:
        return 24;
    default:
    case HAIER_PCM_FORMAT_S16_LE:
    case HAIER_PCM_FORMAT_S16_BE:
        return 16;
    case HAIER_PCM_FORMAT_S8:
        return 8;
    };
}


/**
 * @brief Get the file size 
 * 
 * @param file_name 
 * @return int 
 */

unsigned int get_file_size(char * file_name)
{
    uint32_t size = 0;
    FILE *fp = fopen(file_name,"rb");
    if(fp == NULL){
        printf("open file error\n");
        return -1;
    }
    fseek(fp,0,SEEK_END);
    size = ftell(fp);
    fclose(fp);
    return size;
}

int pcm_translate_to_wav(char * pcm_file,char * wav_file,int channel,int rate)
{
    //wav header info
    struct wav_header header;
    //pcm file size
    uint32_t pcm_file_size = 0; 
    // fp
    FILE *fp_pcm_file,*fp_wav_file;
    // buffer
    unsigned char *read_data;

    header.riff_id = ID_RIFF;
    header.riff_sz = 0;
    header.riff_fmt = ID_WAVE;
    header.fmt_id = ID_FMT;
    header.fmt_sz = 16;
    header.audio_format = FORMAT_PCM;
    header.num_channels = 6;
    header.sample_rate = 16000;

    enum pcm_format_haier format = HAIER_PCM_FORMAT_S16_LE;

    header.bits_per_sample = haier_pcm_format_to_bits(format);
    header.byte_rate = (header.bits_per_sample / 8) * channel * rate;
    header.block_align = channel * (header.bits_per_sample / 8);
    header.data_id = ID_DATA;

    pcm_file_size = get_file_size(pcm_file);

    read_data = (unsigned char *) malloc(pcm_file_size * sizeof(unsigned char));
    if (read_data == NULL)
    {
        printf("malloc error\n");
        return -1;
    }

    header.data_sz = pcm_file_size * header.block_align;
    header.riff_sz = header.data_sz + sizeof(header) - 8;

    fp_pcm_file = fopen(pcm_file, "rb+");
    if(fp_pcm_file == NULL)
    {
        printf("Open pcm file error.\n");
        return -1;
    }
    fp_wav_file = fopen(wav_file, "wb+");
    if(fp_wav_file == NULL)
    {
        printf("Create wav file error.\n");
        return -1;
    }
    //wav file write header info in file first
    fseek(fp_wav_file, 0, SEEK_SET);
    fwrite(&header, sizeof(struct wav_header), 1, fp_wav_file);

    // read data from pcm file to buffer
    fread(read_data,sizeof(unsigned char),pcm_file_size,fp_pcm_file);
    // write data from buffer to wav file 
    fwrite(read_data,sizeof(unsigned char),pcm_file_size,fp_wav_file);

    free(read_data);
    fclose(fp_pcm_file);
    fclose(fp_wav_file);
    return 0;
}

/**
 * @brief wav translate pcm is easy
 *        cut 44 byte data from wav file 
 * 
 * @param wav_file 
 * @param pcm_file 
 * @return int 
 */

int wav_translate_to_pcm(char * wav_file,char *pcm_file)
{
    size_t ret = 0;
    // wav file size 
    uint32_t wav_file_size = 0;
    // fp
    FILE *fp_pcm_file,*fp_wav_file;
    // buffer
    unsigned char *read_data;

    fp_wav_file = fopen(wav_file,"rb");
    fp_pcm_file = fopen(pcm_file,"wb");
    wav_file_size = get_file_size(wav_file);

    if (fp_wav_file == NULL || fp_wav_file == NULL)
    {
        printf("open file error\n");
        return -1;
    }

    read_data = (unsigned char *)malloc(wav_file_size * sizeof(unsigned char));
    if (read_data == NULL)
    {
        printf("malloc error\n");
        return -1;
    }
    // wav文件的指针从头向后移动44字节 wav header 44 byte
    fseek(fp_wav_file,44,SEEK_SET);
    ret = fread(read_data,sizeof(unsigned char),(wav_file_size - 44),fp_wav_file);

    if (ret != wav_file_size - 44)
    {
        printf("count != \n");
        return -1;
    }
    // 写到pcm文件中
    fwrite(read_data,sizeof(unsigned char),(wav_file_size - 44),fp_pcm_file);

    fclose(fp_wav_file);
    fclose(fp_pcm_file);
    free(read_data);
    return 0;
}

int pcm_split_2_channels(char *pcm_files)
{
    unsigned char tmp_data[4] = {0};
    
    FILE *orgin_files = fopen(pcm_files,"rb");
    if (!orgin_files)
    {
        printf("orgin_files null\n");
        return -1;
    }

    FILE *l_files = fopen("l.pcm","wb+");
    if (!l_files)
    {
        printf("l_files null\n");
        return -1;
    }


    FILE *r_files = fopen("r.pcm","wb+");
    if (!r_files)
    {
        printf("r_files null\n");
        return -1;
    }

    while(!feof(orgin_files))
    {
        fread(tmp_data,1,4,orgin_files);

        fwrite(tmp_data,1,2,l_files);

        fwrite(tmp_data+2,1,2,r_files);
    }
    
    fclose(orgin_files);
    fclose(l_files);
    fclose(r_files)
;
    return 0;
}

int get_pcm_data_by_channel_num(char *file_name,int channel_num,int max_channel_num,int len,short *data)
{
    FILE *fp;
    short *read_buffer;

    if (data == NULL){

        printf("data is NULL\n");
        return -1;
    }

    fp = fopen(file_name,"rb");

    if (!fp){

        printf("get_pcm_data_by_channel_num fopen error\n");
        return -1;
    }

    read_buffer = (short *)malloc((max_channel_num * len) * sizeof(short));
    if (!read_buffer){

        printf("get_pcm_data_by_channel_num malloc error\n");
        return -1;
    }

    size_t read_num = fread(read_buffer,sizeof(short),max_channel_num * len,fp);
    if (read_num != max_channel_num * len){
        
        printf("get_pcm_data_by_channel_num error\n");
        return -1;
    }

    for (int i=0;i<len;i++){

        memcpy(&data[i],&read_buffer[(max_channel_num * i) + channel_num],sizeof(short));
    }

    free(read_buffer);
    fclose(fp);
    return 0;
}

int save_data_to_file(char *file_name , short *data,int len)
{
    FILE *fp ;

    fp  = fopen(file_name,"w+");
    if (!fp){

        printf("fopen error %s\n",file_name);
        return -1;
    }

    size_t num = fwrite(data,sizeof(short),len,fp);

    if(num != len){

        printf("fwrite error\n");
        return -1;
    }

    fclose(fp);
    return 0;
}
